package org.gridlab.gridsphere.services.visualization;

import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.LinkedList;
import java.lang.reflect.Array;
import javax.xml.parsers.*;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.*;

/**
 * Implementacja interfejsu DataProvider, która ładuje dane z pliku XML
 * @author Krzysztof Goj
 */
public class XMLDataProvider extends DefaultDataProvider {
    /** Handler zdarzeń dla parsera SAX */
    class MyHandler extends DefaultHandler {
        /**
         * metoda wywoływana po napotkaniu na znacznik otwierający
         */
	public void startElement(String uri, String localName, String qName, Attributes attributes) {
            if(sthWentBad) return;

            if(qName.equals("data")) {
                try {
                    valueDataTypeName = attributes.getValue("type");
                    valueDataType = Class.forName(valueDataTypeName);
                    if(valueDataTypeName.startsWith("[L"))
                        setDataType(Class.forName("[" + valueDataTypeName));
                    else
                        setDataType(Class.forName("[L" + valueDataTypeName + ';'));
                } catch(ClassNotFoundException e) {
                    sthWentBad = true;
                }
            }
            else if(qName.equals("series")) {
                currSeriesName = attributes.getValue("name");
                currSeries = new LinkedList();
            }
            else if(qName.equals("value")) {
                if(valueDataType == NamedNumber.class) {
                    Number nr = new Double(attributes.getValue("x"));
                    String label = attributes.getValue("label");
                    currSeries.add(new NamedNumber(label, nr));
                }
                else if(Object[].class.isAssignableFrom(valueDataType)) {
                    List tmp = new LinkedList();
                    Double[] darr = new Double[1];
                    String xyz[] = {"x", "y", "z"};

                    for(int i=0; i<3; ++i) 
                        if(attributes.getValue(xyz[i]) != null) {
                            Number nr = new Double(attributes.getValue(xyz[i]));
                            tmp.add(nr);
                        }
                    currSeries.add(tmp.toArray(darr));
                }
            }
	}
        /**
         * Metoda wywoływana po napotkaniu na znacznik zamykający
         */
        public void endElement(String uri, String localName, String qName) {
            if(sthWentBad) return;
            if(qName.equals("series")) {
                addSeries(currSeries.toArray((Object[])Array.newInstance(valueDataType, 1)), currSeriesName);
            }
	}
    }
    /**
     * Parsuje dane XML dostarczone ze strumienia is
     * @param is strumień zawierający dane do parsowania 
     * @throws java.lang.ClassNotFoundException rzucane, gdy w pliku XML pojawi się nazwa nieistniejącej klasy
     * @throws javax.xml.parsers.SAXException gdy coś się nie powiedzie przy parsingu
     * @throws java.io.IOException gdy coś się nie powiedzie przy odczycie danych ze strumienia
     * @throws javax.xml.parsers.ParserConfigurationException gdy coś się nie powiedzie przy konfiguracji parsera
     */
    public void parse(InputStream is)  throws ClassNotFoundException, SAXException, ParserConfigurationException, IOException {
	ContentHandler myHandler = new MyHandler();
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setValidating(true);
        SAXParser parser = factory.newSAXParser();
        parser.parse(is, new MyHandler());
        if(sthWentBad)
            throw new ClassNotFoundException(valueDataTypeName);
        setCreationTime();
    }

    /**
     * Konsturktor. Parsuje dane ze strumienia is
     * @throws java.lang.ClassNotFoundException rzucane, gdy w pliku XML pojawi się nazwa nieistniejącej klasy
     * @throws javax.xml.parsers.SAXException gdy coś się nie powiedzie przy parsingu
     * @throws java.io.IOException gdy coś się nie powiedzie przy odczycie danych ze strumienia
     * @throws javax.xml.parsers.ParserConfigurationException gdy coś się nie powiedzie przy konfiguracji parsera
     */
    public XMLDataProvider(InputStream is) throws ClassNotFoundException, SAXException, ParserConfigurationException, IOException {
        super();
        parse(is);
    }

    public XMLDataProvider() {
        super();
    }

    private List currSeries = new LinkedList();
    private String currSeriesName;
    private Class valueDataType;
    private String valueDataTypeName;
    private boolean sthWentBad = false;
}


